package abel.wlmr.model.type;

import java.util.ArrayList;
import java.util.List;

import abel.wlmr.model.WlmrException;
import abel.wlmr.model.WlmrField;

/**
 * B.4.3.4.4.1 数据单元标识定义
 * 数据单元标识由信息点标识 DA 和信息类标识 DT 组成，表示信息点和信息类型，格式见图 B.7。
 * 信息点 DA
 * DA1 D7 D6 D5 D4 D3 D2 D1 D0
 * DA2 D7 D6 D5 D4 D3 D2 D1 D0
 * 信息类 DT
 * DT1 D7 D6 D5 D4 D3 D2 D1 D0
 * DT2 D7 D6 D5 D4 D3 D2 D1 D0
 * 
 * B.4.3.4.4.2 信息点DA
 * 信息点 DA 由信息点元 DA1 和信息点组 DA2 两个字节构成。
 * DA2 为信息点组， 采用 8 位二进制编码方式； DA1 为某一信息点组 DA2 对应的 1～8 个信息点，采
 * 用 1 位二进制表示。采用 DA2 与 DA1 共同表示方式，可构成信息点标识 pn（n=1～2040） 共 2040 个；
 * 格式见图 B.8。
 * 信息点组 DA2 信息点元 DA1
 * D7～D0 D7 D6 D5 D4 D3 D2 D1 D0
 * 1 p8 p7 p6 p5 p4 p3 p2 p1
 * 2 p16 p15 p14 p13 p12 p11 p10 p9
 * 3 p24 p23 p22 p21 p20 p19 p18 p17
 * …… …… …… …… …… …… …… …… ……
 * 255 P2040 P2039 P2038 P2037 P2036 P2035 P2034 P2033
 * 
 * defalt to p0.
 * 
 * @author Abel
 *
 */
public class DataAddrField extends WlmrField {
    private short atom;

    private short group;

    /**
     * default to p0.
     */
    public DataAddrField() {
        super(2);
    }

    private void verifyGroupAtom() {
        if ((group == 0 && atom != 0) || (group != 0 && atom == 0)) {
            throw new IllegalArgumentException("group & atom of UnitAddrField should be both 0 or none-0");
        }
    }

    public List<Short> addrList() {
        verifyGroupAtom();

        List<Short> lst = new ArrayList<>();
        if (group == 0) {
            lst.add((short) 0);
            return lst;
        }

        for (int i = 0; i < 8; i++) {
            int bit = 0x01 & (atom >> i);
            if (bit == 1) {
                int v = ((group - 1) << 3) + (i + 1);
                lst.add((short) v);
            }
        }

        return lst;
    }

    public void setAtomList(boolean[] atomList) {
        if (atomList == null || atomList.length != 8) {
            throw new IllegalArgumentException("atom list should be of length 8.");
        }

        atom = 0;
        for (int i = 0; i < 8; i++) {
            atom |= (atomList[i] ? 1 : 0) << i;
        }
    }

    public void setAddrList(short... addrList) {
        if (addrList == null || addrList.length > 8) {
            throw new IllegalArgumentException("unit list should be of length [0,8].");
        }

        if (addrList.length == 0 || (addrList.length == 1 && addrList[0] == 0)) {
            group = 0;
            atom = 0;
            return;
        }

        for (int i = 0; i < addrList.length; i++) {
            if (addrList[i] < 1 || addrList[i] > 2040) {
                throw new IllegalArgumentException("Unit addr should be [1,2040]");
            }
        }

        short g = (short) ((addrList[0] - 1) >> 3);
        for (int i = 1; i < addrList.length; i++) {
            if (g != (addrList[i] - 1) >> 3) {
                throw new IllegalArgumentException("unit should be of same group.");
            }
        }

        this.group = (short) (g + 1);
        this.atom = 0;
        for (int i = 0; i < addrList.length; i++) {
            atom |= 0x01 << ((addrList[i] - 1) & 0x07);
        }
    }

    public short firstAddr() {
        verifyGroupAtom();

        if (group == 0) {
            return 0;
        }

        for (int i = 0; i < 8; i++) {
            int v1 = atom >> i & 0x01;
            if (v1 == 1) {
                return (short) (((group - 1) << 3) + (i + 1));
            }
        }

        return 0;
    }

    @Override
    public int getValue() {
        return atom | (group << 8);
    }

    @Override
    public void parseValue(int v) throws WlmrException {
        atom = (short) (0xFF & v);
        group = (short) ((0xFF00 & v) >> 8);

        try {
            verifyGroupAtom();
        } catch (IllegalArgumentException e) {
            throw new WlmrException("invalid data address: " + v);
        }
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return String.format("{atom:%s, group:%s, firstUnit():%s}", atom, group, firstAddr());
    }

    /**
     * @return the da1
     */
    public short getAtom() {
        return atom;
    }

    /**
     * @param a
     *            the atom to set
     */
    public void setAtom(short a) {
        if (a > 255 || a < 0) {
            throw new IllegalArgumentException("atom should be [0, 255]");
        }

        this.atom = a;
    }

    /**
     * @return the da2
     */
    public short getGroup() {
        return group;
    }

    /**
     * @param g
     *            the group to set
     */
    public void setGroup(short g) {
        if (g > 255 || g < 0) {
            throw new IllegalArgumentException("group should be [0, 255]");
        }

        this.group = g;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null || !(obj instanceof DataAddrField)) {
            return false;
        }

        DataAddrField v = (DataAddrField) obj;
        return v.atom == atom && v.group == group;
    }

}
